package away3d.bounds


import away3d.core.base.Geometry ;
import away3d.core.base.SubGeometry ;
import away3d.errors.AbstractMethodError ;
import away3d.primitives.WireframePrimitiveBase ;
import flash.geom.Matrix3D ;
import flash.geom.Vector3D ;



/**
 * An abstract base class for all bounding volume classes. It should not be instantiated directly.
 */
class BoundingVolumeBase
{
    
    
    private  var _min                   : Vector3D ;
    private  var _max                   : Vector3D ;
    private  var _aabbPoints            : Vector<Float> ;
    private  var _aabbPointsDirty       : Bool ;
    private  var _boundingRenderable    : WireframePrimitiveBase ;
    
    
    
    /**
     * Creates a new BoundingVolumeBase object
     */
    public function new()
    {
        
        _aabbPoints         = new Vector() ;
        _aabbPointsDirty    = true ;
        _min                = new Vector3D() ;
        _max                = new Vector3D() ;
        
    }
    
    
    public var boundingRenderable( get_boundingRenderable, null ) : WireframePrimitiveBase
    
    
    private function get_boundingRenderable() : WireframePrimitiveBase
    {
        
        if( !_boundingRenderable )
        {
            
            _boundingRenderable = createBoundingRenderable() ;
            updateBoundingRenderable() ;
            
        }
        
        return _boundingRenderable ;
        
    }
    
    
    private function updateBoundingRenderable()
    {
        
        throw new AbstractMethodError() ;
        
    }
    
    
    private function createBoundingRenderable() : WireframePrimitiveBase
    {
        
        throw new AbstractMethodError() ;
        
    }
    
    
    /**
     * Sets the bounds to zero size.
     */
    public function nullify()
    {
        
        _min.x = _min.y = _min.z = 0 ;
        _max.x = _max.y = _max.z = 0 ;
        _aabbPointsDirty = true ;
        if( _boundingRenderable ) updateBoundingRenderable() ;
        
    }
    
    
    public function disposeRenderable()
    {
        
        if( _boundingRenderable ) _boundingRenderable.dispose( true ) ;
        _boundingRenderable = null ;
        
    }
    
    
    public var max( get_max, null ) : Vector3D<Float> ;
    
    
    /**
     * The maximum extrema of the bounds
     */
    private function get_max() : Vector3D<Float>
    {
        
        return _max ;
        
    }
    
    
    public var min( get_min, null ) : Vector3D<Float> ;
    
    
    /**
     * The minimum extrema of the bounds
     */
    private function get_min() : Vector3D<Float>
    {
        
        return _min ;
        
    }
    
    
    /**
     * Updates the bounds to fit a list of vertices
     * @param vertices A Vector<Float> of vertex data to be bounded.
     */
    public function fromVertices( vertices : Vector<Float> )
    {
        
        var i       : Int  ;
        var len     = vertices.length ;
        var minX    : Float ;
        var minY    : Float ;
        var minZ    : Float ;
        var maxX    : Float ;
        var maxY    : Float ;
        var maxZ    : Float ;
        
        if( len == 0 )
        {
            
            nullify() ;
            return ;
            
        }
        
        var v : Float ;
        
        minX = maxX = vertices[ uint( i++ ) ] ;
        minY = maxY = vertices[ uint( i++ ) ] ;
        minZ = maxZ = vertices[ uint( i++ ) ] ;

        while ( i < len )
        {
            
            v = vertices[ i++ ] ;
            
            if( v < minX ) minX = v ;
            else if( v > maxX ) maxX = v ;
            
            v = vertices[i++] ;
            
            if( v < minY ) minY = v ;
            else if( v > maxY ) maxY = v ;
            
            v = vertices[i++] ;
            
            if( v < minZ ) minZ = v ;
            else if( v > maxZ ) maxZ = v ;
            
        }
        
        fromExtremes( minX, minY, minZ, maxX, maxY, maxZ ) ;
        
    }
    
    
    /**
     * Updates the bounds to fit a Geometry object.
     * @param geometry The Geometry object to be bounded.
     */
    public function fromGeometry( geometry : Geometry )
    {
        var subs        = geometry.subGeometries ;
        var j           : Int  ;
        var lenS        = subs.length ;
        var lenV        : Int  ;
        var vertices    : Vector<Float> ;
        var i           : Int  ;
        var v           : Float ;
        var minX        : Float ;
        var minY        : Float ;
        var minZ        : Float ;
        var maxX        : Float ;
        var maxY        : Float ;
        var maxZ        : Float ;
        
        //TODO: Don't think you can have INFINITY infinity?
        minX = minY = minZ = Number.POSITIVE_INFINITY ;
        maxX = maxY = maxZ = Number.NEGATIVE_INFINITY ;
        
        while ( j < lenS )
        {
            
            vertices    = subs[ j++ ].vertexData ;
            lenV        = vertices.length ;
            i           = 0 ;
            
            while ( i < lenV )
            {
                
                v = vertices[ i++ ] ;
                
                if( v < minX ) minX = v ;
                else if( v > maxX ) maxX = v ;
                
                v = vertices[ i++ ] ;
                
                if( v < minY ) minY = v ;
                else if( v > maxY ) maxY = v ;
                
                v = vertices[ i++ ] ;
                
                if( v < minZ ) minZ = v ;
                else if( v > maxZ ) maxZ = v ;
                
            }
            
        }
        
        
        //TODO: Don't think you can have INFINITY infinity?
        if( minX == Number.POSITIVE_INFINITY )
        {
            
            minX = minY = minZ = maxX = maxY = maxZ = 0 ;
            
        }
        
        fromExtremes( minX, minY, minZ, maxX, maxY, maxZ ) ;
        
    }
    
    
    /**
     * Sets the bound to fit a given sphere.
     * @param center The center of the sphere to be bounded
     * @param radius The radius of the sphere to be bounded
     */
    public function fromSphere( center : Vector3D, radius : Float )
    {
        
        // this is BETTER overridden, because most volumes will have shortcuts for this
        // but then again, sphere already overrides it, and if we'd call "fromSphere", it'd probably need a sphere bound anyway
        fromExtremes( center.x - radius, center.y - radius, center.z - radius, center.x + radius, center.y + radius, center.z + radius ) ;
        
    }
    
    
    /**
     * Sets the bounds to the given extrema.
     */
    public function fromExtremes( minX : Float, minY : Float, minZ : Float, maxX : Float, maxY : Float, maxZ : Float )
    {
        
        _min.x              = minX ;
        _min.y              = minY ;
        _min.z              = minZ ;
        _max.x              = maxX ;
        _max.y              = maxY ;
        _max.z              = maxZ ;
        _aabbPointsDirty    = true ;
        
        if( _boundingRenderable ) updateBoundingRenderable() ;
        
    }
    
    
    /**
     * Tests if the bounds are in the camera frustum.
     * @param mvpMatrix The model view projection matrix for the object to which this bounding box belongs.
     * @return True if the bounding box is at least partially inside the frustum
     */
    public function isInFrustum( mvpMatrix : Matrix3D ) : Bool
    {
        
        throw new AbstractMethodError() ;
        return false ;
        
    }
    
    
    /*public function classifyAgainstPlane( plane : Plane3D ) : int
     {
     throw new AbstractMethodError() ;
     return -1 ;
     }*/
    
    
    /**
     * Clones the current BoundingVolume object
     * @return An exact duplicate of this object
     */
    public function clone() : BoundingVolumeBase
    {
        
        throw new AbstractMethodError() ;
        
    }
    
    
    private function get_aabbPoints() : Vector<Float>
    {
        
        if( _aabbPointsDirty )
        {
            
            updateAABBPoints() ;
            
        }
        
        return _aabbPoints ;
        
    }
    
    
    public function intersectsLine( p : Vector3D, dir : Vector3D ) : Bool
    {
        
        throw new AbstractMethodError() ;
        return false ;
        
    }
    
    
    public function intersectsRay( p : Vector3D, dir : Vector3D ) : Bool
    {
        
        throw new AbstractMethodError() ;
        return false ;
        
    }
    
    
    private  function updateAABBPoints()
    {
        
        var i       : Int  ;
        var maxX    : Float = _max.x ;
        var maxY    : Float = _max.y ;
        var maxZ    : Float = _max.z ;
        var minX    : Float = _min.x ;
        var minY    : Float = _min.y ;
        var minZ    : Float = _min.z ;
        
        _aabbPoints[ i++ ]  = minX ;
        _aabbPoints[ i++ ]  = minY ;
        _aabbPoints[ i++ ]  = minZ ;
        _aabbPoints[ i++ ]  = maxX ;
        _aabbPoints[ i++ ]  = minY ;
        _aabbPoints[ i++ ]  = minZ ;
        _aabbPoints[ i++ ]  = minX ;
        _aabbPoints[ i++ ]  = maxY ;
        _aabbPoints[ i++ ]  = minZ ;
        _aabbPoints[ i++ ]  = maxX ;
        _aabbPoints[ i++ ]  = maxY ;
        _aabbPoints[ i++ ]  = minZ ;
        _aabbPoints[ i++ ]  = minX ;
        _aabbPoints[ i++ ]  = minY ;
        _aabbPoints[ i++ ]  = maxZ ;
        _aabbPoints[ i++ ]  = maxX ;
        _aabbPoints[ i++ ]  = minY ;
        _aabbPoints[ i++ ]  = maxZ ;
        _aabbPoints[ i++ ]  = minX ;
        _aabbPoints[ i++ ]  = maxY ;
        _aabbPoints[ i++ ]  = maxZ ;
        _aabbPoints[ i++ ]  = maxX ;
        _aabbPoints[ i++ ]  = maxY ;
        _aabbPoints[ i ]    = maxZ ;
        _aabbPointsDirty    = false ;
        
    }
    
    
}